package com.lbh.audio;

import com.jogamp.openal.AL;
import com.jogamp.openal.ALException;
import com.jogamp.openal.ALFactory;
import com.jogamp.openal.util.ALut;

import java.io.IOException;
import java.nio.ByteBuffer;

/***
 * project:JRuntime
 * package:com.lbh.audio
 *
 * @author user:lenovo QQ:1601078366
 * @date 2021/10/20 23:19
 * @version V1.0
 */
public class OpenAL {

    private static AL al;

    private static int[] buffers;
    private static int[] sources;

    private static float[][] sourcePos;
    private static float[][] sourceVel;
    private static float[] listenerPos = { 0.0f, 0.0f, 0.0f };
    private static float[] listenerVel = { 0.0f, 0.0f, 0.0f };
    private static float[] listenerOri = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f };
    // forward up

    private static boolean initialized = false;
    public static boolean initialize() {
        if (initialized) {
            return true;
        }

        // Initialize OpenAL and clear the error bit.
        try {
            ALut.alutInit();
            al = ALFactory.getAL();
            al.alGetError();
        } catch (ALException e) {
            e.printStackTrace();
            return false;
        }

        setListenerValues();

        initialized = true;
        return true;
    }

    public static boolean uninitialize(){
        if (!initialized){
            return true;
        }

        killAllData();
        ALut.alutExit();

        initialized = false;
        return true;
    }

    public static int loadAudioFiles(String[] path){
        //variables to load into
        int[] format = new int[1];
        int[] size = new int[1];
        ByteBuffer[] data = new ByteBuffer[1];
        int[] freq = new int[1];
        int[] loop = new int[1];

        if(!initialize()){
            return AL.AL_FALSE;
        }

        buffers = new int[path.length];
        al.alGenBuffers(path.length, buffers, 0);
        if (detectError()){
            return AL.AL_FALSE;
        }

        for (int i = 0; i < path.length; i++) {
            ALut.alutLoadWAVFile(
                    OpenAL.class.getClassLoader().getResourceAsStream(path[i]),
                    format,
                    data,
                    size,
                    freq,
                    loop);
            al.alBufferData(buffers[i],
                    format[0],
                    data[0],
                    size[0],
                    freq[0]);
        }

        // bind buffers into audio sources
        sources = new int[path.length];
        al.alGenSources(path.length, sources, 0);

        sourcePos = new float[path.length][];
        sourceVel = new float[path.length][];

        for (int i = 0; i < path.length; i++) {
            // similar to OpenGL
            sourcePos[i] = new float[]{1.0F, 0.0F, 0.0F};
            sourceVel[i] = new float[]{0.0F, 0.0F, 0.0F};

            // default options
            al.alSourcei(sources[i], AL.AL_BUFFER, buffers[i]);
            al.alSourcef(sources[i], AL.AL_PITCH, 1.0F);
            al.alSourcef(sources[i], AL.AL_GAIN, 1.0F);
            al.alSourcefv(sources[i], AL.AL_POSITION, sourcePos[i], 0);
            al.alSourcefv(sources[i], AL.AL_VELOCITY, sourceVel[i], 0);
            al.alSourcei(sources[i], AL.AL_LOOPING, AL.AL_TRUE);
        }

        return AL.AL_TRUE;
    }

    public static boolean detectError(){
        int err = al.alGetError();
        if (err != AL.AL_NO_ERROR){
            System.out.println("OpenAL Error: Code " + err);
            return true;
        }
        return false;
    }

    public static void audioPlay(int index, int loop){
        al.alSourcei(sources[index], AL.AL_LOOPING, loop);
        al.alSourcePlay(sources[index]);
        detectError();
    }

    public static void audioPause(int index){
        al.alSourcePause(sources[index]);
    }

    public static void audioStop(int index){
        al.alSourceStop(sources[index]);
    }

    public static void setListenerValues() {
        al.alListenerfv(AL.AL_POSITION, listenerPos, 0);
        al.alListenerfv(AL.AL_VELOCITY, listenerVel, 0);
        al.alListenerfv(AL.AL_ORIENTATION, listenerOri, 0);
    }

    private static void killAllData() {
        if (buffers != null) {
            al.alDeleteBuffers(buffers.length, buffers, 0);
        }
        if (sources != null) {
            al.alDeleteSources(sources.length, sources, 0);
        }
    }
}
